﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using XXF.BaseService.ServiceCenter.SystemRuntime;

namespace XXF.BaseService.ServiceCenter.Service.Protocol
{
    public class ProtocolsToThrift
    {
        private const string namespacetemplate = @"
{$注释}
namespace csharp {$命名空间正常}.Thrift
namespace java {$命名空间小写}.thrift

{$类集合}
{$服务集合}
";
        private const string classtemplate = @"
/** {$类注释} */
struct {$类名} {   
    {$属性集合}
  }";
        private const string propertytemplate = @"
    /** {$属性注释} */
    {$属性序号}:{$属性类型} {$属性名}
";
        private const string servertemplate = @"
/** {$服务注释}*/
service {$服务名}Thrift {  
    {$方法集合}
}
";
        private const string methodtemplate = @"
    /** {$方法注释}*/
    {$方法返回类型} {$方法名}({$方法参数集合})
";
        private const string methodparamstemplate = @"{$参数序号}:{$参数类型} {$参数名},";

        private string remarktemplate = @"
# Thrift支持的数据类型
# bool（bool）: 布尔类型(TRUE or FALSE)
# byte（byte）: 8位带符号整数
# i16（short）: 16位带符号整数
# i32（int）: 32位带符号整数
# i64（long）: 64位带符号整数
# double（double）: 64位浮点数
# string（string）: 采用UTF-8编码的字符串
# binary（byte[]）：未经过编码的字节流
# list(List<>)：
# map（Dictionary<,>）：
";

        static ProtocolsToThrift()
        {
            try
            {
                DirectoryInfo dir = new DirectoryInfo(AppDomain.CurrentDomain.BaseDirectory.Trim('\\') + "\\servicetemp\\");
                if (dir.Exists)
                {
                    dir.Delete(true);
                }
            }
            catch (Exception exp)
            {
                //throw new ServiceCenterException(exp.Message);
            }
        }
        /// <summary>
        /// 将自定义协议->thrift.txt文本内容
        /// </summary>
        /// <param name="protocal"></param>
        /// <param name="namespacename"></param>
        /// <returns></returns>
        public string To(ServiceProtocal protocal, string namespacename)
        {

            string classlist = "";
            foreach (var e in protocal.EntityProtocals)
            {
                string propertylist = "";
                for (int i = 0; i < e.PropertyParams.Count; i++)
                {
                    var pty = e.PropertyParams[i];
                    var doc = e.PropertyDocs[i];
                    propertylist += propertytemplate.Replace("{$属性序号}", i + 1 + "")
                        .Replace("{$属性类型}", Type(pty.TypeName))
                        .Replace("{$属性名}", pty.Name)
                        .Replace("{$属性注释}", string.Format("属性:{0},描述:{1}", doc.Text, doc.Description));
                }
                string classcontent = classtemplate.Replace("{$类名}", e.Name)
                     .Replace("{$属性集合}", propertylist)
                     .Replace("{$类注释}", string.Format("类:{0},描述:{1}", e.EntityDoc.Text, e.EntityDoc.Description));
                classlist += classcontent;
            }
            string methodlist = "";
            foreach (var m in protocal.MethodProtocols)
            {
                string inputparamlist = "";
                for (int i = 0; i < m.InputParams.Count; i++)
                {
                    var ps = m.InputParams[i];
                    inputparamlist += methodparamstemplate.Replace("{$参数序号}", i + 1 + "")
                        .Replace("{$参数类型}", Type(ps.TypeName))
                        .Replace("{$参数名}", ps.Name);
                }
                string methodcontent = methodtemplate.Replace("{$方法名}", m.Name)
                     .Replace("{$方法返回类型}", Type(m.ReturnParam.TypeName))
                     .Replace("{$方法参数集合}", inputparamlist.TrimEnd(','))
                     .Replace("{$方法注释}", string.Format("方法:{0},描述:{1}", m.MethodDoc.Text, m.MethodDoc.Description));
                methodlist += methodcontent;
            }
            string servicelist = servertemplate.Replace("{$服务名}", protocal.Name)
                                            .Replace("{$服务注释}", string.Format("服务:{0},描述:{1}", protocal.ServiceDoc.Text, protocal.ServiceDoc.Description))
                                            .Replace("{$方法集合}", methodlist);
            string content = namespacetemplate.Replace("{$命名空间正常}", namespacename)
                                              .Replace("{$命名空间小写}", namespacename.ToLower())
                                              .Replace("{$类集合}", classlist)
                                              .Replace("{$服务集合}", servicelist)
                                              .Replace("{$注释}", remarktemplate);
            return content;

        }
        /// <summary>
        /// Thrift协议信息（内部类）
        /// </summary>
        private class ThriftInfo
        {
            public string ServiceName { get; set; }
            public string FullName { get; set; }
            public string FileName { get; set; }
            public string Dir { get; set; }
            public string Code { get; set; }
        }
        /// <summary>
        /// 通过thrift.exe将自定义协议->thrift.txt->C#协议代码
        /// </summary>
        /// <param name="protocal"></param>
        /// <param name="namespacename"></param>
        /// <returns></returns>
        public Type ToType(ServiceProtocal protocal, string namespacename)
        {

            string thriftexefile = "";
            DirectoryInfo dir = new DirectoryInfo(AppDomain.CurrentDomain.BaseDirectory.Trim('\\') + "\\");
            FileInfo[] files = dir.GetFiles();
            for (int i = 0; i < files.Length; i++)//访问当前目录的文件
            {
                if (files[i].Name.ToLower().Contains("thrift") && files[i].Extension.ToString().ToLower() == ".exe")
                {
                    thriftexefile = files[i].FullName;
                }
            }
            if (string.IsNullOrWhiteSpace(thriftexefile))
            {
                //下载thrift.exe至本地
                var path = AppDomain.CurrentDomain.BaseDirectory.Trim('\\') + "\\" + "thrift-0.9.2.exe";
                CommonHelper.DownloadThriftExe(path);
                thriftexefile = path;
                if (!System.IO.File.Exists(path))
                {
                    throw new ServiceCenter.SystemRuntime.ServiceCenterException("当前目录下未找到thrift.exe类似的文件用于生成thrift协议");
                }
            }
            string dirto = dir.FullName.Trim('\\') + "\\servicetemp\\";
            XXF.Common.IOHelper.CreateDirectory(dirto);

            string thriftdir = dirto + protocal.Name + "\\" + Math.Abs(Guid.NewGuid().GetHashCode()).ToString() + "\\";
            XXF.Common.IOHelper.CreateDirectory(thriftdir);

            string code = To(protocal, namespacename); ThriftInfo thriftfile = new ThriftInfo();

            string filename = protocal.Name + ".thrift.txt";
            string filepath = thriftdir + filename;
            System.IO.File.WriteAllText(filepath, code);
            thriftfile = new ThriftInfo() { Code = code, Dir = thriftdir, FullName = filepath, FileName = filename, ServiceName = protocal.Name };

            Process p = new Process();
            p.StartInfo.FileName = SafePath(thriftexefile);
            LogHelper.Log(-1, EnumLogType.Service, p.StartInfo.FileName);
            p.StartInfo.Arguments = "--gen csharp -o " + SafePath(thriftdir.TrimEnd('\\')) + " -r " + SafePath(thriftfile.FullName);
            LogHelper.Log(-1, EnumLogType.Service, p.StartInfo.Arguments);
            p.StartInfo.UseShellExecute = false;
            p.StartInfo.CreateNoWindow = true;
            //p.StartInfo.RedirectStandardOutput = false;
            //p.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
            p.StartInfo.RedirectStandardError = true;
            p.StartInfo.RedirectStandardOutput = true;//

            p.StartInfo.RedirectStandardInput = true;//
            p.ErrorDataReceived += (object sender, DataReceivedEventArgs e) =>
                        {

                        };
            p.Start();
            string line = "";
            using (var reader = p.StandardOutput)
            {
                line = reader.ReadToEnd();
            }
            p.WaitForExit();
            if (p.ExitCode != 0)
            {

                throw new ServiceCenter.SystemRuntime.ServiceCenterException(string.Format("thrift.exe 编译{0}协议出错:{1}", thriftfile.FullName, line));

            }

            DirectoryInfo dthriftdir = new DirectoryInfo(thriftdir);
            var fs = dthriftdir.GetFiles("*.cs", SearchOption.AllDirectories);
            List<string> cscodes = new List<string>();
            foreach (var f in fs)
            {
                cscodes.Add(System.IO.File.ReadAllText(f.FullName));
            }
            //string proxycode = serviceproxytemplate.Replace("{$servicename}", t.ServiceName).Replace("{$servicefullname}", services[index].FullName)
            //    .Replace("{$namespace}", namespacename).Replace("{$serviceThriftfullname}", namespacename + ".Thrift." + t.ServiceName + "Thrift");
            //cscodes.Add(proxycode);
            //DynamicProxy.DynamicCompilerProvider dc = new DynamicProxy.DynamicCompilerProvider();
            //var type = dc.Compiler(new List<string>(new[] { "Thrift.dll", services[index].Assembly.Location}), namespacename + "." + t.ServiceName + "Proxy", cscodes.ToArray());


            DynamicProxy.DynamicCompilerProvider dc = new DynamicProxy.DynamicCompilerProvider();
            var type = dc.Compiler(new List<string>(new[] { "Thrift.dll" }), namespacename + ".Thrift." + thriftfile.ServiceName + "Thrift", cscodes.ToArray());
            if (type == null)
                throw new ServiceCenter.SystemRuntime.ServiceCenterException(string.Format("thrift.exe 生成协议失败:{1}", thriftfile.FullName));
            return type;
        }




        /// <summary>
        /// Quote paths with spaces
        /// </summary>
        private string SafePath(string path)
        {
            if (path.Contains(' ') && !path.StartsWith("\""))
            {
                return "\"" + path + "\"";
            }
            return path;
        }

        /// <summary>
        /// 自定义协议类型转thrift协议类型
        /// </summary>
        /// <param name="nettype"></param>
        /// <returns></returns>
        private string Type(string nettype)
        {
            /* thrift->java 类型对照
bool（boolean）: 布尔类型(TRUE or FALSE)
byte（byte）: 8位带符号整数
i16（short）: 16位带符号整数
i32（int）: 32位带符号整数
i64（long）: 64位带符号整数
double（double）: 64位浮点数
string（String）: 采用UTF-8编码的字符串
binary（ByteBuffer）：未经过编码的字节流
             */
            var t = nettype.ToLower();
            if (t == "int32" || t == "int")
            {
                return "i32";
            }
            if (t == "string")
            {
                return "string";
            }
            if (t == "short" || t == "int16")
            {
                return "i16";
            }
            if (t == "long" || t == "int64")
            {
                return "i64";
            }
            if (t == "byte[]")
            {
                return "binary";
            }
            if (t == "void")
            {
                return "void";
            }
            if (IsGenericType(nettype))
            {
                if (nettype.StartsWith("List<") && nettype.EndsWith(">"))
                {
                    return string.Format("list<{0}>", GenericTypeParamters(nettype)[0]);
                }
                if (nettype.StartsWith("Dictionary<") && nettype.EndsWith(">"))
                {
                    return string.Format("map<{0},{1}>", GenericTypeParamters(nettype)[0], GenericTypeParamters(nettype)[1]);
                }
            }
            return nettype;
        }
        /// <summary>
        /// 泛型
        /// </summary>
        /// <param name="nettype"></param>
        /// <returns></returns>
        private bool IsGenericType(string nettype)
        {
            if (nettype.Contains("<") && nettype.Contains(">"))
            {
                return true;
            }
            else
                return false;
        }
        /// <summary>
        /// 泛型参数
        /// </summary>
        /// <param name="nettype"></param>
        /// <returns></returns>
        private string[] GenericTypeParamters(string nettype)
        {
            List<string> ss = new List<string>();
            if (IsGenericType(nettype))
            {
                foreach (var s in nettype.Substring(nettype.IndexOf('<') + 1, nettype.IndexOf('>') - nettype.IndexOf('<') - 1).Split(','))
                {
                    if (!string.IsNullOrWhiteSpace(s))
                        ss.Add(Type(s.Trim()));
                }
            }
            return ss.ToArray();
        }
    }
}
